package cn.edu.sdju.fengguoyu;

import cn.edu.sdju.fengguoyu.proto.*;
import io.grpc.ManagedChannel;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.*;

@Service
public class RegisterServers {
    public static int idCounter = 1;
    HashMap<String, ArrayList<RegisterServerInfo>> rawData = new HashMap<>();
    HashMap<Integer, RegisterServerInfo> idServerMap =  new HashMap<>();
    @Autowired
    ObserverConnectionsPool observerConnectionsPool;
    @Autowired
    WaitingMap waitingMap;
    public HashMap<String, ArrayList<RegisterServerInfo>> getRawData() {
        return rawData;
    }
    public RegisterServerInfo getBestServer(String named) {
        int minRank  = Integer.MAX_VALUE;
        RegisterServerInfo bestServer = null;
        if (!rawData.containsKey(named)) return null;
        for (RegisterServerInfo i: rawData.get(named)
             ) {
            if (i.getRank() <= minRank) {
                minRank = i.getRank();
                bestServer = i;
            }
        }
        return bestServer;
    }
    public int register(RegisterInfo info) {
        if (!rawData.containsKey(info.getServiceName())) {
            ArrayList<RegisterServerInfo> registerServerInfos = new ArrayList<>();
            rawData.put(info.getServiceName(), registerServerInfos);
        }
        RegisterServerInfo i = new RegisterServerInfo();
        i.setTimestamp(new Date().getTime());
        i.setService(info.getService());
        i.setRank(info.getService().getRank());
        i.setServerName(info.getServiceName());
        i.setId(RegisterServers.idCounter++);
        rawData.get(info.getServiceName()).add(i);
        idServerMap.put(i.getId(), i);
        // TODO
        for (ManagedChannel channel: observerConnectionsPool.observerChannels.values()
        ) {
            ObserverMessageBroadcastGrpc.ObserverMessageBroadcastBlockingStub stub = ObserverMessageBroadcastGrpc.newBlockingStub(channel);
            stub.sendNewServiceMessage(NewServiceMessage.newBuilder().
                    setId(i.getId()).
                    setType("NODE").
                    setServerNamed(info.getServiceName()).
                    setIpAddress(info.getService().getIpAddress()).
                    setPort(info.getService().getPort())
                    .build()
            );
        }
        try {
            if (waitingMap.streamObserverHashMap.containsKey(info.getServiceName())) {
                waitingMap.streamObserverHashMap.get(
                                info.getServiceName()).
                        onNext(
                                ServiceResponse.newBuilder()
                                        .setIpAddress(info.getService().getIpAddress())
                                        .setPort(info.getService().getPort())
                                        .setId(i.getId())
                                        .build()
                        );
                waitingMap.streamObserverHashMap.get(info.getServiceName()).onCompleted();
            }
        } catch (IllegalStateException ignored) {

        }

        return i.getId();
    }

    public void updateRank(int id, int newRank) {
        idServerMap.get(id).setRank(newRank);
        for (ManagedChannel channel:
                observerConnectionsPool.observerChannels.values()) {
            ObserverMessageBroadcastGrpc.ObserverMessageBroadcastBlockingStub stub = ObserverMessageBroadcastGrpc.newBlockingStub(channel);
            stub.sendServiceRank(ServiceRankMessage.newBuilder().setServiceId(id).setRank(newRank).build());
        }
    }

    public void removeService(String named, int id) {
        ArrayList<RegisterServerInfo> services = rawData.get(named);
        for (int i = 0; i < services.size(); i++) {
            if (services.get(i).getId() == id) {
                services.remove(i);
                break;
            }
        }
        idServerMap.remove(id);
    }

    public RegisterServerInfo getServerById(int id) {
        return idServerMap.get(id);
    }
}
